#pragma once
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <vector>
#include <functional>

#include "Thread.hpp"
#include "ThreadPool.hpp"
#include "nocopy.hpp"
#include "InetAddr.hpp"
#include "LockGuard.hpp"

#define CONV(addr_ptr) ((sockaddr*)(addr_ptr))
using task_t = std::function<void()>;  //用std::bind使得参数为空

class UdpServer : public nocopy
{
public:
    UdpServer(uint16_t port)
    :_port(port)
    {
        pthread_mutex_init(&_user_mutex, nullptr);
    }
    void Init()
    {
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if(_sockfd < 0)
        {
            exit(1);
        }

        sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons(_port);

        int n = bind(_sockfd, CONV(&server), sizeof server);
        if(n < 0)
        {
            exit(2);
        }
        ThreadPool<task_t>::GetInstance()->Start(); //这个是创建线程的shart
    }

    void AddOnlineUser(InetAddr& addr)
    {
        LockGuard lock(&_user_mutex);
        for(auto &e : _online_user)
            if(e == addr) return;

        _online_user.push_back(addr);
        std::cout << addr.GetDebug() << " joined online chat" << std::endl;
    }
    void Route(int sockfd, std::string message)
    {
        LockGuard lock(&_user_mutex);
        for(auto& user : _online_user)
            sendto(sockfd, message.c_str(), message.size(), 0, CONV(&user._addr), sizeof user._addr);
    }
    void Start()
    {
        char buffer[1024];
        while(true)
        {
            sockaddr_in peer;
            socklen_t len = sizeof peer; //输入输出型，所以要初始化
            int n = recvfrom(_sockfd, buffer, sizeof buffer - 1, 0, CONV(&peer), &len);
            if(n > 0)
            {
                InetAddr addr(peer);
                AddOnlineUser(addr);
                buffer[n] = 0;

                std::string message = addr.GetDebug();
                message += " say: ";
                message += buffer;
                task_t task = std::bind(&UdpServer::Route, this, _sockfd, message);

                ThreadPool<task_t>::GetInstance()->Push(task);
            }
        }
    }
private:
    uint16_t _port;
    int _sockfd;

    std::vector<InetAddr> _online_user;
    pthread_mutex_t _user_mutex;
};